/**
*  \file    li_ion.c
*
*  \brief   Lithium Ion Project
*
*  \author  Thomas Wenzel <thomas.wenzel@linde-mh.de>
*
*/

/*===============================================[ private includes  ]================================================*/
#include "li_ion.h"
#include "li_ion_term.h"                                                                       /* Service-Parameters  */
#include "../cfg/lithium_ion_cfg.h"                                                            /* Configuration       */
#include "../mlib/middle.h"                                                                    /* middle_s16(), ..    */
#include "li_ion_sdo.h"                                                                        /* Li-Io SDO transfers */
#include "../specs/specs.h"
#include "../mlib/max.h"                                                                       /* max2_f32(), ..      */
#include <math.h>                                                                              /* fabsf(), ..         */
#include <string.h>                                                                            /* memset(), ..        */
#include "../canmsg/canmsg.h"
#include "../input/input.h"
#include "../app/app.h"
#include "../parameter/production.h"
#include "../parameter/service.h"
/*===============================================[ private defines   ]================================================*/
#define EQU_TIMER         (50)                   /* 50 * 10ms = 500ms = 0.5s */

/*===============================================[ private datatypes ]================================================*/

/*===============================================[ private variables ]================================================*/
static F32 f32IntegralBattVolt;                  /* 02.12.2014/TW */
static F32 f32IntegralBattCurrent;               /* 02.12.2014/TW */
static S16 s16TimerUpdateRWT;                    /* 02.12.2014/TW */

/*===============================================[ public variables  ]================================================*/
LITHIUM_ION_IO sLiIon;
F32 ba_qBat_rpReqLiIon;

/*===============================================[ private functions ]================================================*/

/**
*  filter function (1st order lag) for temperature signals
*  \brief   basic 1st order lag initialized to actual temperature in the first secunds
*        NB time constant is quite high so that faulty step of temperature signal
*           may not cause sudden (unexpected) acceleration or deceleration of the truck
*  \param   input  temperature signal Te to be filtered
*  \param   output filtered temperature Tef
*/

__inline static void li_ion_filt_10ms(F32 Te, F32 *Tef, F32 const tau)
{
   if (in_Time() < 2.0f)
   {
      *Tef = Te;                          /* no filter at begin                  */
   }
   else
   {
      *Tef += (Te - *Tef) * (TS / tau);   /* 1st order low pass filter otherwise */
   }
}

/**
*  vdInput_LiIon    Lithium-Ion-Service-Function: Fill input structure with system-data
*                   called from app_li_ion_10ms()
*/
static VO vdInput_LiIon( VO )
{
   /* ======================================== */
   /* From input-module:                       */
   /* ======================================== */
   sLiIon.f32BatteryVolt_K41 = 0;                                              /* Filtered :K41 - input */

#if (EQU_HARDWARE_CODE == EQU_FITTED)
   sLiIon.f32CodeLiIon = 0;                                                  /* :K84 - input */
   sLiIon.in_bLiIon    = sLiIon.f32CodeLiIon <  3.0f  ?  1  :  0  ;
#endif

}

/**
*  s16LiIon_StateMachine    Lithium-Ion-Service-Function: State-Machine to detect lithium-ion-/or lead-acid
*                           at power-on
*                           called from app_li_ion_10ms()
*/
static S16 s16LiIon_StateMachine( VO )
{
   switch ( sLiIon.s16State )
   {
      /* ================================================================================= */
      /* Initial state after Power-On: Check Pin :K84 AND Heartbeat (cycle-time of 40ms !) */
      /* ================================================================================= */
      case EQU_LI_ION_INIT:
      {
         /* ================================ */
         /* No BMS-Heartbeat and :K84 opened */
         /* ================================ */
         if (    (    (IS_K41_GREATER_10V)                                                                 /* BR38x: Pin :K41 wird ignoriert / BR1120: Pin :K41 wird ausgewertet*/
                   && (pre_operational != sLiIon.CAN_LiIoBMSB_State)
                   && (operational     != sLiIon.CAN_LiIoBMSB_State)
#if (EQU_HARDWARE_CODE == EQU_FITTED)
                   && !sLiIon.in_bLiIon                                                                    /* :K84 = 0/opened: Codierung Lead-Acid */
#endif
                 )
              || ( TRUE != specs->s16WithLithiumIon ) )                                                    /* Trucktype does not support Lithium-Ion (24V !) */
         {
            sLiIon.s16Counter = middle_s16 (-EQU_LI_ION_80ms, (sLiIon.s16Counter+1), EQU_LI_ION_80ms) ;    /* 80ms up -> Lead Acid*/
         }
         /* =============================================== */
         /* Heartbeats from BMS-System or :K84 = 1/grounded */
         /* =============================================== */
         else
         {
            sLiIon.s16Counter = middle_s16 (-EQU_LI_ION_80ms, (sLiIon.s16Counter-1), EQU_LI_ION_80ms) ;    /* 80ms down -> Lithium-Ion */
         }

         if ( EQU_LI_ION_80ms <= sLiIon.s16Counter )                                         /* Heartbeats and :K84 opened for 80ms after power-on */
         {
            sLiIon.s16State   = EQU_IS_LEAD_ACID_BATTERY;                                    /* ... it must be a lead-acid battery                   */
            sLiIon.s16Counter = 0;                                                           /* ... clear counter on transition                      */
         }

         if ( -EQU_LI_ION_80ms >= sLiIon.s16Counter )                                        /* Pin :K84 is grounded for 80ms after power-on         */
                                                                                             /* or Heartbeat BMS-System present                      */
         {
            sLiIon.s16State   = EQU_LI_ION_AUTHENT_START;                                    /* ... it can be a li-ion battery                       */
            sLiIon.s16Counter = 0;                                                           /* ... clear counter on transition                      */
         }

         break;
      }

      /* ===================================================== */
      /* Pin :K41 > 10.0V when OS starts for at least 80ms AND */
      /* Pin :K84 > 10.0V when OS starts for at least 80ms     */
      /* ===================================================== */
      case EQU_IS_LEAD_ACID_BATTERY:
      {
         sLiIon.s16BatteryType = EQU_BATT_TYPE_LEAD_ACID;                                    /* ... set Battery-Type: LEAD-ACID-BATTERY              */
         sLiIon.s16State       = EQU_LI_ION_FINISHED;                                        /* ... signal to SUPERVISOR "Known battery-type"        */
         sLiIon.s16Counter     = 0;                                                          /* ... clear counter on transition                      */
         break;
      }

      /* ================================================================================================= */
      /* Pin :K84 < 3.0V or :K84 grounded when OS starts for at least 80ms -> chance to have li-ion fitted */
      /* ================================================================================================= */
      case EQU_LI_ION_AUTHENT_START:
      {
         sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_5000ms) ;      /* Counter - managment*/


         if (    (    (pre_operational == sLiIon.CAN_LiIoBMSB_State)                         /* ... SDO-communication ready,                         */
                   || (operational     == sLiIon.CAN_LiIoBMSB_State) )                       /* ... PDO-communication ready,                         */
#if (EQU_HARDWARE_CODE == EQU_FITTED)
              && sLiIon.in_bLiIon                                                            /* and :K84 = 1/grounded: Codierung Li-Ion              */
#endif
            )

         {
            sLiIon.s16State    = EQU_LI_ION_SDO_SEND_BATTERY_VOLTAGE;                        /* ... next SDO-Download of nominal battery-volltage    */
            sLiIon.s16Counter  = 0;                                                          /* ... clear counter on transition                      */
            sLiIon.s16SDORetry = 0;                                                          /* ... clear SDO-retries at entry-point                 */
         }

         if ( EQU_LI_ION_5000ms <= sLiIon.s16Counter )                                       /* Timeout No heartbeats within 5s                      */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_001;                                        /* ... No Heartbeats                                    */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;                                                           /* ... clear counter on transition                      */
         }

         break;
      }

      /* ============================================================ */
      /* CAN-Heartbeats with status PRE-/OPERATIONAL received         */
      /* OR                                                           */
      /* SDO-Download-retry because failed before                     */
      /* ============================================================ */
      case EQU_LI_ION_SDO_SEND_BATTERY_VOLTAGE:
      {
         li_ion_sdo_send_nominal_voltage(li_ion_callback, (U8)te_BattType_par);              /* SDO-Download of nominal battery-voltage              */

         sLiIon.s16State   = EQU_LI_ION_SDO_WAIT_BATTERY_VOLTAGE;                            /* ... wait for finished SDO-Download                   */
         sLiIon.s16Counter = 0;                                                              /* ... clear counter on transition                      */
         sLiIon.s16SDORetry++;                                                               /* ... Increment number of SDO-Download-tries           */

         break;
      }

      /* ================================================================================= */
      /* SDO download was started in previous cycle, now wait for finish                   */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_WAIT_BATTERY_VOLTAGE:
      {
         sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_100ms) ;       /* Counter - managment                                  */

         if ( EQU_LI_ION_100ms <= sLiIon.s16Counter )                                        /* Give SDO-download a chance of 100ms                  */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_005;                                        /* ERROR 5: timeout / battery-voltage                   */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         else if (2 < sLiIon.s16SDORetry)                                                    /* Only 3x tries / battery-voltage ...                  */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_006;                                        /* ERROR 6: Nr of retries / battery-voltage exceeded    */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }

         break;
      }

      /* ================================================================================= */
      /* This state is set at callback-function of SDO-download, when succeeded            */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_BATTERY_VOLTAGE_FINISHED:
      {
         sLiIon.s16State    = EQU_LI_ION_SDO_SEND_VIN;                                       /* ... SDO-Download of Vehicle-Identification-Nr        */
         sLiIon.s16Counter  = 0;                                                             /* ... clear counter on transition                      */
         sLiIon.s16SDORetry = 0;                                                             /* ... clear SDO-retries at entry-point                 */
         break;
      }

      /* ================================================================================= */
      /* SDO-Download of Vehicle-Identification-Number                                     */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_SEND_VIN:
      {
         if ( 0x00 != sLiIon.CAN_vin[0] )
         {
            li_ion_sdo_send_VIN(li_ion_callback, (CH *)&sLiIon.CAN_vin[0], (U8)VIN_STRING_SIZE);

            sLiIon.s16State   = EQU_LI_ION_SDO_WAIT_VIN;                                     /* ... wait for finished SDO-Download                   */
            sLiIon.s16Counter = 0;                                                           /* ... clear counter on transition                      */
            sLiIon.s16SDORetry++;                                                            /* ... Increment number of SDO-Download-tries           */

         }
         else
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_007;                                        /* ERROR 7: VIN error                                   */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         break;
      }

      /* ================================================================================= */
      /* SDO download was started in previous cycle, now wait for finish                   */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_WAIT_VIN:
      {
         sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_100ms) ;       /* Counter - managment                                  */

         if ( EQU_LI_ION_100ms <= sLiIon.s16Counter )                                        /* Give SDO-download a chance of 100ms                  */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_008;                                        /* ERROR 8: timeout / VIN                               */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         else if (2 < sLiIon.s16SDORetry)                                                    /* Only 3x tries ...                                    */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_009;                                        /* ERROR 9: Number of retries exceeded / VIN            */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         break;
      }

      /* ================================================================================= */
      /* This state is set at callback-function of SDO-download, when succeeded            */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_VIN_FINISHED:
      {
         sLiIon.s16State    = EQU_LI_ION_SDO_SEND_CAN_SCHEME;                                /* ... SDO-Download of CAN-Scheme                       */
         sLiIon.s16Counter  = 0;                                                             /* ... clear counter on transition                      */
         sLiIon.s16SDORetry = 0;                                                             /* ... clear SDO-retries at entry-point                 */
         break;
      }

      /* ================================================================================= */
      /* SDO-Download of CAN-Scheme                                                        */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_SEND_CAN_SCHEME:
      {
         li_ion_sdo_send_can_scheme(li_ion_callback, DEF_CURRENT_CAN_SCHEME);                /* SDO-Download of truck-specific CAN-scheme            */

         sLiIon.s16State   = EQU_LI_ION_SDO_WAIT_CAN_SCHEME;                                 /* ... wait for finished SDO-Download                   */
         sLiIon.s16Counter = 0;                                                              /* ... clear counter on transition                      */
         sLiIon.s16SDORetry++;                                                               /* ... Increment number of SDO-Download-tries           */
         break;
      }

      /* ================================================================================= */
      /* SDO download was started in previous cycle, now wait for finish                   */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_WAIT_CAN_SCHEME:
      {
         sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_100ms) ;       /* Counter - managment                                  */

         if ( EQU_LI_ION_100ms <= sLiIon.s16Counter )                                        /* Give SDO-download a chance of 100ms                  */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_010;                                        /* ERROR 8: timeout / CAN-scheme                        */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         else if (2 < sLiIon.s16SDORetry)                                                    /* Only 3x tries ...                                    */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_011;                                        /* ERROR 9: Number of retries exceeded / CAN-scheme     */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         break;
      }

      /* ================================================================================= */
      /* This state is set at callback-function of SDO-download, when succeeded            */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_CAN_SCHEME_FINISHED:
      {
         sLiIon.s16State    = EQU_LI_ION_SDO_REQUEST_BATTERY_SERIAL_NR;                      /* ... SDO-Upload of battery's serial number            */
         sLiIon.s16Counter  = 0;                                                             /* ... clear counter on transition                      */
         sLiIon.s16SDORetry = 0;                                                             /* ... clear SDO-retries at entry-point                 */
         break;
      }

      /* ================================================================================= */
      /* SDO-Upload of battery's serial number:                                            */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_REQUEST_BATTERY_SERIAL_NR:
      {
         li_ion_sdo_request_battery_serial_number(li_ion_callback, &sLiIon.CAN_BatterySerialNo);

         sLiIon.s16State   = EQU_LI_ION_SDO_WAIT_BATTERY_SERIAL_NR;                          /* ... wait for finished SDO-Download                   */
         sLiIon.s16Counter = 0;                                                              /* ... clear counter on transition                      */
         sLiIon.s16SDORetry++;                                                               /* ... Increment number of SDO-Download-tries           */
         break;
      }

      /* ================================================================================= */
      /* SDO upload was started in previous cycle, now wait for finish                     */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_WAIT_BATTERY_SERIAL_NR:
      {
         sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_100ms) ;       /* Counter - managment                                  */

         if ( EQU_LI_ION_100ms <= sLiIon.s16Counter )                                        /* Give SDO-download a chance of 100ms                  */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_012;                                        /* ERROR 12: timeout / Battery's serial-nr              */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         else if (2 < sLiIon.s16SDORetry)                                                    /* Only 3x tries ...                                    */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_013;                                        /* ERROR 13: Number of retries exceeded / serial-nr     */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         break;
      }

      /* ================================================================================= */
      /* Battery's serial number was received on CAN                                       */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_BATTERY_SERIAL_NR_FINISHED:
      {
         if ( EQU_LI_ION_INVALID_SERIAL_NUMBER != sLiIon.CAN_BatterySerialNo )
         {
            sLiIon.s16State    = EQU_LI_ION_SDO_REQUEST_BATTERY_ENERGY;                      /* ... SDO-Upload of battery's energy                   */
            sLiIon.s16Counter  = 0;                                                          /* ... clear counter on transition                      */
            sLiIon.s16SDORetry = 0;                                                          /* ... clear SDO-retries at entry-point                 */

         }
         else
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_014;                                        /* ERROR 14: Invalid battery-serial-nr     */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         break;
      }

      /* ================================================================================= */
      /* SDO-Upload of battery's energy [Wh]:                                              */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_REQUEST_BATTERY_ENERGY:
      {
         li_ion_sdo_request_battery_energy(li_ion_callback, &sLiIon.CAN_BatteryEnergy);

         sLiIon.s16State   = EQU_LI_ION_SDO_WAIT_BATTERY_ENERGY;                             /* ... wait for finished SDO-Download                   */
         sLiIon.s16Counter = 0;                                                              /* ... clear counter on transition                      */
         sLiIon.s16SDORetry++;                                                               /* ... Increment number of SDO-Download-tries           */
         break;
      }

      /* ================================================================================= */
      /* SDO upload was started in previous cycle, now wait for finish                     */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_WAIT_BATTERY_ENERGY:
      {
         sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_100ms) ;       /* Counter - managment                                  */

         if ( EQU_LI_ION_100ms <= sLiIon.s16Counter )                                        /* Give SDO-download a chance of 100ms                  */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_015;                                        /* ERROR 15: timeout / Battery's serial-nr              */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         else if (2 < sLiIon.s16SDORetry)                                                    /* Only 3x tries ...                                    */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_016;                                        /* ERROR 16: Number of retries exceeded / serial-nr     */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         break;
      }

      /* ================================================================================= */
      /* Battery's serial number was received on CAN                                       */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_BATTERY_ENERGY_FINISHED:
      {
         if ( 0 != sLiIon.CAN_BatteryEnergy )
         {
            sLiIon.s16State    = EQU_LI_ION_SEND_MODULE_CTRL;                                /* ... SDO-Upload of battery's energy finished          */
            sLiIon.s16Counter  = 0;                                                          /* ... clear counter on transition                      */
            sLiIon.s16SDORetry = 0;                                                          /* ... clear SDO-retries at entry-point                 */
         }
         else
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_017;                                        /* ERROR 17: Invalid battery-energy                    */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         break;
      }

      /* ================================================================================= */
      /* Request ModuleCtrl via can_func.c                                                 */
      /* ================================================================================= */
      case EQU_LI_ION_SEND_MODULE_CTRL:
      {
         sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_50ms) ;        /* Counter - managment                                  */

         if ( EQU_LI_ION_50ms <= sLiIon.s16Counter )                                         /* Give ModuleCtrl a chance of 50ms                     */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_018;                                        /* ERROR 18: timeout / Module-Ctrl                      */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         break;
      }

      /* ================================================================================= */
      /* ModuleCtrl processed by can_func.c                                                */
      /* ================================================================================= */
      case EQU_LI_ION_MODULE_CTRL_ACK:
      {
         sLiIon.s16State       = EQU_LI_ION_IDENTIFIED;                                      /* ... wait for battery's ready-state                   */
         break;
      }

      /* ================================================================================= */
      /* Battery's energy [Wh] was received on CAN                                         */
      /* ================================================================================= */
      case EQU_LI_ION_IDENTIFIED:
      {
         sLiIon.s16State       = EQU_LI_ION_WAIT_FOR_BATT_READY;                             /* ... wait for battery's ready-state                   */
         sLiIon.s16Counter     = 0;                                                          /* ... clear counter on transition                      */
         break;
      }

      /* ================================================================================= */
      /* Wait for battery is ready received via PDO1 / 0x18D / Byte 7: (100ms-cycle)       */
      /* ================================================================================= */
      case EQU_LI_ION_WAIT_FOR_BATT_READY:
      {
         // TODO: If possible reduce delay from 1000ms
         sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_1000ms) ;      /* Counter - managment                                  */

         /* Check Ready Bit on PDO/0x18D Byte 7: 0x04*/
         if (    (sLiIon.CAN_LiIoBMS_BatteryStatus == STATUS_LI_ION_BATTERY_READY)
              || (sLiIon.CAN_LiIoBMS_BatteryStatus == STATUS_LI_ION_BATTERY_DRIVING)
              || (sLiIon.CAN_LiIoBMS_BatteryStatus == STATUS_LI_ION_BATTERY_CHARGING) )
         {
            sLiIon.s16State    = EQU_LI_ION_SDO_SEND_BATTERY_ON;                             /* ... send command to battery via CAN-Layer            */
            sLiIon.s16Counter  = 0;                                                          /* ... clear counter on transition                      */
            sLiIon.s16SDORetry = 0;                                                          /* ... clear SDO-retries at entry-point                 */
         }
         else
         {
            // TODO: If possible reduce delay from 1000ms
            if ( EQU_LI_ION_1000ms <= sLiIon.s16Counter )                                    /* Give ModuleCtrl a chance of 500ms (!)                */
            {
               sLiIon.s16Error   = EQU_LI_ION_ERROR_002;                                     /* ERROR 2: timeout / Status battery ready              */
               sLiIon.s16State   = EQU_LI_ION_ERROR;
               sLiIon.s16Counter = 0;
            }
         }
         break;
      }

      /* ================================================================================= */
      /* Turn on battery via SDO-Download                                                  */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_SEND_BATTERY_ON:
      {
         li_ion_sdo_turn_battery_on(li_ion_callback);                                        /* SDO-Download of battery turn-on                     */

         sLiIon.s16State   = EQU_LI_ION_SDO_WAIT_BATTERY_ON;                                 /* ... wait for finished SDO-Download                   */
         sLiIon.s16Counter = 0;                                                              /* ... clear counter on transition                      */
         sLiIon.s16SDORetry++;                                                               /* ... Increment number of SDO-Download-tries           */

         break;
      }

      /* ================================================================================= */
      /* Wait for SDO-Download finished                                                    */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_WAIT_BATTERY_ON:
      {
         sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_100ms) ;       /* Counter - managment                                  */

         if ( EQU_LI_ION_100ms <= sLiIon.s16Counter )                                        /* Give SDO-download a chance of 100ms                  */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_019;                                        /* ERROR 19: timeout / battery turn-on                  */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }
         else if (2 < sLiIon.s16SDORetry)                                                    /* Only 3x tries / battery turn-on ...                  */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_020;                                        /* ERROR 20: Nr of retries / battery turn-on exceeded   */
            sLiIon.s16State   = EQU_LI_ION_ERROR;
            sLiIon.s16Counter = 0;
         }

         break;
      }

      /* ================================================================================= */
      /* This state is set at callback-function of SDO-download, when succeeded            */
      /* ================================================================================= */
      case EQU_LI_ION_SDO_BATTERY_ON_FINISHED:
      {
         sLiIon.s16State    = EQU_LI_ION_CHECK_VOLTAGE;                                      /* ... Finally check current battery-voltage on :K41    */
         sLiIon.s16Counter  = 0;                                                             /* ... clear counter on transition                      */
         sLiIon.s16SDORetry = 0;                                                             /* ... clear SDO-retries at entry-point                 */
         break;
      }

      /* ================================================================================= */
      /* Finally check Li-Ion-Battery-Voltage on :K41                                      */
      /* PDO1-cycle-time: 100ms                                                            */
      /* ================================================================================= */
      case EQU_LI_ION_CHECK_VOLTAGE:
      {
         /* 150ms-Filter for :K41 (BDI-Battery-Input) */
         if (    (10.0F < sLiIon.f32BatteryVolt_K41)
              && (sLiIon.CAN_LiIoBMS_BatteryStatus == STATUS_LI_ION_BATTERY_DRIVING) )
         {
            sLiIon.s16Counter = middle_s16 (0, (sLiIon.s16Counter+1), EQU_LI_ION_150ms) ;
         }
         else
         {
            sLiIon.s16Counter = middle_s16 (-EQU_LI_ION_1000ms, (sLiIon.s16Counter-1), 0) ;
         }

         if ( EQU_LI_ION_150ms <= sLiIon.s16Counter )                                        /* Pin :K41 is greater than 10V for 150ms after LI-ION is turned on */
         {
            sLiIon.s16State   = EQU_IS_LI_ION_BATTERY;                                       /* ... it is a turned on li-ion battery with :K41 > 10V */
            sLiIon.s16Counter = 0;                                                           /* ... clear counter on transition                      */
         }

         if ( -EQU_LI_ION_1000ms >= sLiIon.s16Counter )                                      /* Pin :K41 is less than 10V for 150ms after power-on    */
         {
            sLiIon.s16Error   = EQU_LI_ION_ERROR_004;                                        /* ... signal there's something wrong */
            sLiIon.s16State   = EQU_LI_ION_ERROR;                                            /* ... it is a turned on li-ion battery with :K41 < 10V */
            sLiIon.s16Counter = 0;                                                           /* ... clear counter on transition                      */
         }
         break;
      }

      /* ================================================================================= */
      /* Pin :K41 > 10.0V when li-ion-battery was turned on for at least 150ms             */
      /* ================================================================================= */
      case EQU_IS_LI_ION_BATTERY:
      {
         sLiIon.s16BatteryType = EQU_BATT_TYPE_LI_ION;                                       /* ... set Battery-Type: LI-ION-BATTERY                 */
         sLiIon.s16State       = EQU_LI_ION_FINISHED;                                        /* ... signal to SUPERVISOR "Known battery-type"        */
         sLiIon.s16Counter     = 0;                                                          /* ... clear counter on transition                      */
         break;
      }

      /* ================================================================================= */
      /* Errors during start-up of lithium-ion-battery-managment-system:                   */
      /* ================================================================================= */
      case EQU_LI_ION_ERROR:
      {
         break;
      }

      /* ================================================================================= */
      /* Either lead-acid- or li-ion-battery                                               */
      /* ================================================================================= */
      case EQU_LI_ION_FINISHED:
      default:
      {
         break;
      }
   }

   return (sLiIon.s16State);
}

/*====================================================================================================================*/

RC app_li_ion_initialize( VO )
{
   sLiIon.f32BatteryVolt_K41 = 0.0F;
   sLiIon.s16BatteryType     = EQU_BATT_TYPE_INVALID;

   sLiIon.s16State           = EQU_LI_ION_INIT;
   sLiIon.s16Error           = EQU_LI_ION_OK;
   sLiIon.s16Counter         = 0;

   li_Xlimphome              = service->li_xlimphome;
   li_DCurrent_100           = service->li_dcurrent_100;
   li_DCurrent_0             = service->li_dcurrent_0;
   li_PowRedStartLiIon       = service->li_powredstartliion;
   li_InitCurrentLiIon       = service->li_initcurrentliion;
   li_LiIoniBatLow           = service->li_icurlow;
   li_LiIoniBatHigh          = service->li_icurhigh;
   

   /* Initialization of BMS-CAN-Receive-Messages: */
   sLiIon.CAN_LiIoBMSB_State        = 0;
   sLiIon.CAN_LiIoBMS_BatteryStatus = STATUS_LI_ION_BATTERY_INVALID_0;
   sLiIon.CAN_BatterySerialNo       = EQU_LI_ION_INVALID_SERIAL_NUMBER;
   sLiIon.CAN_BatteryEnergy         = 0;
   sLiIon.bInitCurrentVoltage       = FALSE;

   sLiIon.CAN_vin[0]                = 0x00;

   memcpy( sLiIon.CAN_vin, production->vin, VIN_STRING_SIZE );

   /* Initialization of BMS SDO client module */
   li_ion_sdo_initialize();

   f32IntegralBattVolt    = 0.0F;               /* 05.12.2014/TW */
   f32IntegralBattCurrent = 0.0F;               /* 02.12.2014/TW */
   s16TimerUpdateRWT      = 0;                  /* 02.12.2014/TW */

   return RC_SUCCESS;
}

/* ATTENTION: do not call another "li_ion_sdo_send_xxx" function in the callback, because the code in li_ion_sdo.c/.h
 * is not reentrant! */
void li_ion_callback(RC sdo_transfer_status, U32 sdo_upload_size, U32 sdo_abort_code)
{
   if (RC_SUCCESS == sdo_transfer_status)
   {
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_BATTERY_VOLTAGE   ) { sLiIon.s16State = EQU_LI_ION_SDO_BATTERY_VOLTAGE_FINISHED;   }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_VIN               ) { sLiIon.s16State = EQU_LI_ION_SDO_VIN_FINISHED;               }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_CAN_SCHEME        ) { sLiIon.s16State = EQU_LI_ION_SDO_CAN_SCHEME_FINISHED;        }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_BATTERY_SERIAL_NR ) { sLiIon.s16State = EQU_LI_ION_SDO_BATTERY_SERIAL_NR_FINISHED; }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_BATTERY_ENERGY    ) { sLiIon.s16State = EQU_LI_ION_SDO_BATTERY_ENERGY_FINISHED;    }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_BATTERY_ON        ) { sLiIon.s16State = EQU_LI_ION_SDO_BATTERY_ON_FINISHED;        }
   }
   else
   {
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_BATTERY_VOLTAGE   ) { sLiIon.s16State = EQU_LI_ION_SDO_SEND_BATTERY_VOLTAGE;      }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_VIN               ) { sLiIon.s16State = EQU_LI_ION_SDO_SEND_VIN;                  }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_CAN_SCHEME        ) { sLiIon.s16State = EQU_LI_ION_SDO_SEND_CAN_SCHEME;           }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_BATTERY_SERIAL_NR ) { sLiIon.s16State = EQU_LI_ION_SDO_REQUEST_BATTERY_SERIAL_NR; }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_BATTERY_ENERGY    ) { sLiIon.s16State = EQU_LI_ION_SDO_REQUEST_BATTERY_ENERGY;    }
      if ( sLiIon.s16State == EQU_LI_ION_SDO_WAIT_BATTERY_ON        ) { sLiIon.s16State = EQU_LI_ION_SDO_SEND_BATTERY_ON;           }
   }
}

RC app_li_ion_10ms( VO )
{
   /* Fill lithium-ion input structure: */
   vdInput_LiIon();
   /* Li-Ion Start-Up-Statemachine */
   sLiIon.s16State = s16LiIon_StateMachine();
   return RC_SUCCESS;
}

VO vdLiIon_CanGetData(S16 s16Mode)
{
   S32 s32Temp;
   U32 u32Temp;

   switch (s16Mode)
   {
      case EQU_LI_ION_CAN_HEARTBEAT:
      {
         sLiIon.CAN_LiIoBMSB_State = input_data.bms_state;                                       /* Heartbeat from BMS */
         break;
      }

      case EQU_LI_ION_CAN_PDO1: /*0x18D / 100ms */
      {
         /* LiIo battery current [mA] */
         /* CAN: PDO1 (0x18D) Byte 0-2: Bits 0 .. 23 / Battery-Current[mA]  */
         /* CAN variable b_LiIoBMS_BatteryCurrent_b must be sign converted from S24 to S32 */

         s32Temp   = input_data.bms_battery_current;                                           /* battery current >= 0 */

         /* ================================================================================================================ */
         /* ACHTUNG: 25.09.2014/TW                                                                                           */
         /* Wenn Strom von der Batterie entnommen wird, dann ist das Vorzeichen negativ (invertiert zu allen bisherigen BR's */
         /* ================================================================================================================ */
         sLiIon.CAN_LiIoBMS_BatteryCurrent_A    = -0.001F * (F32)s32Temp;                     /* convert from [mA] -> [A] */

         u32Temp                                = input_data.bms_battery_volts;                 /* Byte 3-5: Bit 24 .. 47 / unsigned: battery voltage [mV] */
         sLiIon.CAN_LiIoBMS_BatteryVoltage_V    = 0.001F * (F32)(u32Temp & 0x00FFFFFF);       /* convert from [mV] -> [V] */

         sLiIon.CAN_LiIoBMS_BatteryStatus       = input_data.bms_battery_state;                   /* Byte 6:   Bit 48 .. 55 / unsigned: Battery-Status from BMS */
         sLiIon.CAN_LiIoBMS_StateOfHealth       = input_data.bms_soh;                            /* Byte 7:   Bit 56 .. 63 / unsigned: [%] */
         break;
      }

      case EQU_LI_ION_CAN_PDO2: /* 0x28D / 100ms */
      {
         /* Regen-mode: current to battery */
         u32Temp                                         = input_data.bms_max_charge_current2;
         sLiIon.CAN_LiIoBMS_MaxChargeCurrent_2sec_A      = 0.250F * (F32)(u32Temp & 0x0000FFFF); /* LiIo battery max. short-term charging current [A] */

         /* Drive-mode: current from battery */
         u32Temp                                         = input_data.bms_max_discharge_current2;
         sLiIon.CAN_LiIoBMS_MaxDischargeCurrent_2sec_A   = 0.250F * (F32)(u32Temp & 0x0000FFFF); /* LiIo battery max. short-term discharge current [A] */

         /* Regen-mode: current to battery */
         u32Temp                                         = input_data.bms_max_charge_current100;
         sLiIon.CAN_LiIoBMS_MaxChargeCurrent_100sec_A    = 0.250F * (F32)(u32Temp & 0x0000FFFF); /* LiIo battery max. long-term charging current [A] */

         /* Drive-mode: current from battery */
         u32Temp                                         = input_data.bms_max_discharge_current100;
         sLiIon.CAN_LiIoBMS_MaxDischargeCurrent_100sec_A = 0.250F * (F32)(u32Temp & 0x0000FFFF); /* LiIo battery max. long-term discharge current [A] */

         break;
      }

      case EQU_LI_ION_CAN_PDO4: /* 0x48D / 1000ms */
      {
         u32Temp                                  = input_data.bms_min_discharge_volts;            /* Byte 0-2: Bit 0  .. 23 / unsigned: battery min. discharge voltage [mV] */
         sLiIon.CAN_LiIoBMS_MinDischargeVoltage_V = 0.001F * (F32)(u32Temp & 0x00FFFFFF);       /* convert from [mV] -> [V] */

         u32Temp                                  = input_data.bms_max_charge_volts;               /* Byte 3-5: Bit 24 .. 47 / unsigned: battery max. charging voltage [mV] */
         sLiIon.CAN_LiIoBMS_MaxChargeVoltage_V    = 0.001F * (F32)(u32Temp & 0x00FFFFFF);       /* convert from [mV] -> [V] */

         sLiIon.CAN_LiIoBMS_SOCwithoutSOH         = input_data.bms_soc_without_soh;                  /* Byte 6:   Bit 48 .. 55 / unsigned: battery charge (battery health not considered) [%] */
         sLiIon.CAN_LiIoBMS_SOCwithSOH            = input_data.bms_soc_with_soh;                     /* Byte 7:   Bit 56 .. 63 / unsigned: battery charge (battery health considered) [%] */
         break;
      }

      case EQU_LI_ION_CAN_PDO5: /* 0x301 / 100ms */
      {
         u32Temp                                   = input_data.bms_output_volts;               /* Byte 0-2: Bit 0  .. 23 / unsigned: LiIo battery output voltage [mV] */
         sLiIon.CAN_LiIoBMS_OutputVoltage_V        = 0.001F * (F32)(u32Temp & 0x00FFFFFF);

         sLiIon.CAN_LiIoBMS_MaxDischargePower_2sec = input_data.bms_max_discharge_power2 * 10;     /* Byte 3-4: Bit 24 .. 39 / unsigned: LiIo battery max. short-term discharge power [W] */
         sLiIon.CAN_LiIoBMS_MaxChargePower_2sec    = input_data.bms_max_charge_power2    * 10;     /* Byte 5-6: Bit 40 .. 56 / unsigned: LiIo battery max. short-term charging power [W] */
         break;
      }

      default:
      {
         break;
      }
   }
}

F32 f32TransformLiIonSOC(U8 u8SOCwithSOH)
{
   /* ============================ */
   /* 89 .. 100[%] -> 90 .. 100[%] */
   /* ============================ */
   if      (u8SOCwithSOH >= 89)           { return (90.0F + ((F32)u8SOCwithSOH-89.0F)*(100.0F-90.0F)/(100.0F-89.0F ));               }
   /* ============================ */
   /* 78 ..  89[%] -> 80 ..  90[%] */
   /* ============================ */
   else if (u8SOCwithSOH >= 78)           { return (80.0F + ((F32)u8SOCwithSOH-78.0F)*( 90.0F-80.0F)/( 89.0F-78.0F ));               }
   /* ============================ */
   /* 66 ..  78[%] -> 70 ..  80[%] */
   /* ============================ */
   else if (u8SOCwithSOH >= 66)           { return (70.0F + ((F32)u8SOCwithSOH-66.0F)*( 80.0F-70.0F)/( 78.0F-66.0F ));               }
   /* ============================ */
   /* 55 ..  66[%] -> 60 ..  70[%] */
   /* ============================ */
   else if (u8SOCwithSOH >= 55)           { return (60.0F + ((F32)u8SOCwithSOH-55.0F)*( 70.0F-60.0F)/( 66.0F-55.0F ));               }
   /* ============================ */
   /* 44 ..  55[%] -> 50 ..  60[%] */
   /* ============================ */
   else if (u8SOCwithSOH >= 44)           { return (50.0F + ((F32)u8SOCwithSOH-44.0F)*( 60.0F-50.0F)/( 55.0F-44.0F ));               }
   /* ============================ */
   /* 33 ..  44[%] -> 40 ..  50[%] */
   /* ============================ */
   else if (u8SOCwithSOH >= 33)           { return (40.0F + ((F32)u8SOCwithSOH-33.0F)*( 50.0F-40.0F)/( 44.0F-33.0F ));               }
   /* ============================ */
   /* 21 ..  33[%] -> 30 ..  40[%] */
   /* ============================ */
   else if (u8SOCwithSOH >= 21)           { return (30.0F + ((F32)u8SOCwithSOH-21.0F)*( 40.0F-30.0F)/( 33.0F-21.0F ));               }
   /* ============================ */
   /* 10 ..  21[%] -> 25 ..  30[%] */
   /* ============================ */
   else if (u8SOCwithSOH >= 10)           { return (25.0F + ((F32)u8SOCwithSOH-10.0F)*( 30.0F-25.0F)/( 21.0F-10.0F ));               }
   /* ============================================== */
   /* XLIMP ..  10[%] -> 20 ..  25[%]: rot permanent */
   /* ============================================== */
   else if (u8SOCwithSOH >= li_Xlimphome) { return (20.0F + ((F32)u8SOCwithSOH-li_Xlimphome)*( 25.0F-20.0F)/( 10.0F-li_Xlimphome )); }
   /* ========================================================== */
   /* 0 .. XLIMP -> 0 ..  20[%]: rot blinkend + buzzer/T150/T150 */
   /* ========================================================== */
   else                                   { return (((F32)u8SOCwithSOH)*( 20.0F)/( li_Xlimphome ));                                  }

}

F32 f32CalcRemainWorkTime_LiIon(U8  u8SOC,               /* [%] 0 .. 100 ; cyclic 1000ms */
                                F32 ti)                  /* [s] run-time                 */
{
   F32 li_ion_BattCurrent;
   F32 li_ion_BattVolt;
   F32 f32Diff;

   if (u8SOC < 10)
   {
      sLiIon.f32RemWorkTime     = 0.0F;                                                                  /* Less than 10% SOC -> Stop see "Anforderungsheft" */
      sLiIon.f32RemWorkTimeFilt = 0.0F;                                                                  /* Less than 10% SOC -> Stop see "Anforderungsheft" */
   }
   else
   {
                                      /* Nominal[Wh]           * SOC[%]          / 100 */
      sLiIon.f32RWTActualEnergy_Wh  = sLiIon.CAN_BatteryEnergy * (F32)(u8SOC-10) * 0.01F;                /* [Wh] = [VAh] */

      /* Actual Current from BMS-system: sign inverted by CAN-receive-Function */
      li_ion_BattCurrent = sLiIon.CAN_LiIoBMS_BatteryCurrent_A;                                          /* [A] from PDO1/0x18D/100ms */
      li_ion_BattCurrent = middle_f32(-800.0f, li_ion_BattCurrent, 800.0f);

      /* Noise-Filter: */
      if ( fabsf(li_ion_BattCurrent) < 0.01F )
      {
         li_ion_BattCurrent = 0.0F;
      }

      f32IntegralBattCurrent += li_ion_BattCurrent * 0.01F;                                              /* 10ms-Task/0.01s: [As] */

      /* Actual Voltage from BMS-system: */
      li_ion_BattVolt    = sLiIon.CAN_LiIoBMS_BatteryVoltage_V;                                          /* [V] from PDO1/0x18D/100ms */
      li_ion_BattVolt    = middle_f32(0, li_ion_BattVolt, 0);

      f32IntegralBattVolt += li_ion_BattVolt * 0.01F;                                                    /* 10ms-Task/0.01s: [Vs] */

      /* Update Managment: Every 500[ms] remaining work-time is updated */
      s16TimerUpdateRWT += 1;

      if (    (s16TimerUpdateRWT < 0)
           || (s16TimerUpdateRWT >= EQU_TIMER) )
      {
         s16TimerUpdateRWT = 0;                                                                          /* Reset counter            */

         sLiIon.f32BattVoltAvrg    = f32IntegralBattVolt    / (EQU_TIMER*0.01F);                         /* [Vs]/0.5s        = [V]     */
         sLiIon.f32BattCurrentAvrg = f32IntegralBattCurrent / (EQU_TIMER*0.01F);                         /* [As]/0.5s        = [A]     */

         /* ================================ */
         /* Ratelimiter for average-current: */
         /* ================================ */
         if (!sLiIon.bInitCurrentVoltage)                                                                /* New with V3.001; Bugfix non-initialized ..._F - values */
         {
            sLiIon.bInitCurrentVoltage  = TRUE;

            /* Use non-filtered, but averaged */
            sLiIon.f32BattVoltAvrg_F    = sLiIon.f32BattVoltAvrg;
            sLiIon.f32BattCurrentAvrg_F = sLiIon.f32BattCurrentAvrg;
         }
         else
         {
            f32Diff = sLiIon.f32BattCurrentAvrg - sLiIon.f32BattCurrentAvrg_F;

            /* UpRate-Limiter:   50[A/s] = 50[A/1000ms] = 0.5[A/10ms*100] = 0.5F [A/10ms] */
            /* DownRate-Limiter: 50[A/s] = 50[A/1000ms] = 0.5[A/10ms*100] = 0.5F [A/10ms] */
            if      ( f32Diff > 0.5F  ) { f32Diff =  0.5F; }
            else if ( f32Diff < -0.5F ) { f32Diff = -0.5F; }

            /* ramping up/down average current: */
            sLiIon.f32BattCurrentAvrg_F += f32Diff;

            /* ================================ */
            /* Ratelimiter for average voltage: */
            /* ================================ */
            f32Diff = sLiIon.f32BattVoltAvrg - sLiIon.f32BattVoltAvrg_F;

            /* UpRate-Limiter:   10[V/s] = 10[V/1000ms] = 10[V/10ms*100] = 0.1F [V/10ms] */
            /* DownRate-Limiter: 10[V/s] = 10[V/1000ms] = 10[V/10ms*100] = 0.1F [V/10ms] */
            if      ( f32Diff > 0.1F  ) { f32Diff =  0.1F; }
            else if ( f32Diff < -0.1F ) { f32Diff = -0.1F; }

            /* ramping up/down average current: */
            sLiIon.f32BattVoltAvrg_F += f32Diff;
         }

         /* Consumption: */
         if      (sLiIon.f32BattCurrentAvrg_F > (specs->BDICurChar1) )
         {
            sLiIon.f32RemWorkTime = 3600.0F * (sLiIon.f32RWTActualEnergy_Wh/sLiIon.f32BattVoltAvrg_F/(specs->BDICurChar1))        ; /* 3600*[VAh]/[V]/[A] = [s] */
         }
         else if (sLiIon.f32BattCurrentAvrg_F > (F32)li_InitCurrentLiIon )
         {
            sLiIon.f32RemWorkTime = 3600.0F * (sLiIon.f32RWTActualEnergy_Wh/sLiIon.f32BattVoltAvrg_F/sLiIon.f32BattCurrentAvrg_F) ; /* 3600*[VAh]/[V]/[A] = [s] */
         }
         else if (sLiIon.f32BattCurrentAvrg_F > 0.001F)
         {
            sLiIon.f32RemWorkTime = 3600.0F * (sLiIon.f32RWTActualEnergy_Wh/sLiIon.f32BattVoltAvrg_F/((F32)li_InitCurrentLiIon))  ; /* 3600*[VAh]/[V]/[A] = [s] */
         }
         /* Regen: Don't touch remaining Work-Time */
         else
         {
            sLiIon.f32RemWorkTime = ba_dT_BatFlat_sec;                                                    /* Old value */
         }

         /* Reset Integratoren: */
         f32IntegralBattVolt    = 0.0F;
         f32IntegralBattCurrent = 0.0F;
      }
      else
      {
         sLiIon.f32RemWorkTime = ba_dT_BatFlat_sec;                                                       /* Old value */
      }
   }

   if (ti < 3.0F)                                                                                         /* 19.02.2015/TW: at start-up switch to unfiltered */
   {
      /* Filter to avoid dynamic changes of remaining work-time */
      sLiIon.f32RemWorkTimeFilt = sLiIon.f32RemWorkTime;
   }
   else
   {
      /* Note: at 9% -> Remaining-Work-time is cleared !                                          */
      /*       Reduce Filter-Time linear from 15% .. 10% SOC to avoid step to 0min at SOC's end ! */
      if (u8SOC <= 15)
      {
         F32 f32FilterTime;

         /* (15s - 0.5s)/5% * (SOC - 10%) + 0.5s */
         f32FilterTime = middle_f32( 0.5F, ((15.0F - 0.5F)/5.0F * (F32)(u8SOC-10) + 0.5F), 15.0F );

         /* Filter to avoid dynamic changes of remaining work-time */
         li_ion_filt_10ms(sLiIon.f32RemWorkTime, &sLiIon.f32RemWorkTimeFilt, f32FilterTime );             /* 0.5s - 15s - PT1-Filtertime */

      }
      else
      {
         /* Filter to avoid dynamic changes of remaining work-time */
         li_ion_filt_10ms(sLiIon.f32RemWorkTime, &sLiIon.f32RemWorkTimeFilt, 15.0F );                     /* 15s - PT1-Filtertime */
      }
   }

   return sLiIon.f32RemWorkTimeFilt;
}

F32 f32LiIon_DischargeCurrent_Down(
   F32   q      /* actual signal value [A] */
,  F32   q1     /* current threshold [A] for start of power reduction (100% power)  */
,  F32   q0     /* current threshold [A] for end   of power reduction (  0% power)  */
,  S16   s16Cmd /* 1: use filtered input */
)
{
   F32 q1_q0;

   li_ion_filt_10ms(q, &sLiIon.CAN_LiIoBMS_MaxDischargeCurrent_2sec_F, 0.100F );                   /* 0.1s PT1-Filtertime */

   q1_q0 = middle_f32(0.001f, q1 - q0, 1500.0f);                                                   /* Limited to 1500[A] */

   if (s16Cmd != 0)
   {
      /* use filtered signal: */
      return middle_f32(0.0f, (sLiIon.CAN_LiIoBMS_MaxDischargeCurrent_2sec_F - q0) / q1_q0, 1.0f); /* q==q0 => 0  q==q1 => 1  */
   }
   else
   {
      /* use non-filtered signal: */
      return middle_f32(0.0f, (q - q0) / q1_q0, 1.0f);                                             /* q==q0 => 0  q==q1 => 1  */
   }
}

S16 IsLithiumIonBattery(VO)
{
   if (    ( EQU_BATT_TYPE_LI_ION == sLiIon.s16BatteryType )
        && ( TRUE == specs->s16WithLithiumIon ) )
   {
      return TRUE;
   }

   return FALSE;
}

S16 IsLiIonBootUpFinished(VO)
{
   if (EQU_LI_ION_FINISHED == sLiIon.s16State)
   {
      return TRUE;
   }

   return FALSE;
}

/***********************************************************************************************************************
*                             Copyright 2011 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
